#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <memory>
using namespace std;

#define BEGINS(x) namespace x {
#define ENDS(x) } // end of namespace x

BEGINS(test1)

class A {
public:
    A() {
        cout << "default constructor" << endl;
    }
    int x, y;
    ~A() {
        cout << "destructor" << endl;
    }
};

int main() {
    shared_ptr<A> p = make_shared<A>();
    cout << "p.use_count = " << p.use_count() << endl;

    shared_ptr<A> q = p;
    cout << "p.use_count = " << p.use_count() << endl;
    cout << "q.use_count = " << q.use_count() << endl;
    p = nullptr;
    cout << "q.use_count = " << q.use_count() << endl;
    q = nullptr;
    cout << "end main" << endl;

    return 0;
}
ENDS(test1)

BEGINS(test2)

class A {
public:
    A() {
        cout << "default constructor" << endl;
    }
    ~A() {
        cout << "destructor" << endl;
    }
    shared_ptr<A> p;
};

int main() {
    shared_ptr<A> p = make_shared<A>();
    shared_ptr<A> q = make_shared<A>();
    p->p = q;
    q->p = p;
    p = nullptr;
    q = nullptr;
    // 两个对象没有在main函数执行前析构
    cout << "end main" << endl;
    return 0;
}

ENDS(test2)

BEGINS(test3)

class A {
public:
    A() {
        cout << "default constructor" << endl;
    }
    int x, y;
    ~A() {
        cout << "destructor" << endl;
    }
};

class ptr_data {
public:
    ptr_data(A *ptr, int *cnt = nullptr)
    : ptr(ptr), cnt(cnt) {
        if (cnt == nullptr) {
            this->cnt = new int(1);
        }
    }
    ~ptr_data() {
        decrease_one();
        if ((*cnt) == 0) delete cnt;
    }
    void increase_one() const {
        (*cnt)++;
        return ;
    }
    void decrease_one() const {
        (*cnt)--;
        if (*cnt == 0) delete ptr;
        return ;
    }
    bool operator==(const ptr_data &obj) const {
        return obj.ptr == ptr && obj.cnt == cnt;
    }

    A *ptr;
    int *cnt;
};

class shared_ptr {
public:
    shared_ptr(A *ptr) : p(ptr) {}
    shared_ptr(const shared_ptr &obj) : p(obj.p) {
        this->p.increase_one();
    }
    ~shared_ptr() {
        p.decrease_one();
    }
    A *operator->() { return p.ptr; }
    A &operator*() { return *(p.ptr); }
    shared_ptr &operator=(const shared_ptr &obj) {
        if (obj.p == p) return *this;
        p.decrease_one();
        p = obj.p;
        p.increase_one();
        return *this;
    }
    int use_count() { return *(p.cnt); }
private:
    ptr_data p;
};

shared_ptr make_shared() {
    return shared_ptr(new A());
}

ostream &operator<<(ostream &out, const A &a) {
    cout << "class A : " << a.x << ", " << a.y;
    return out;
}

int main() {
    shared_ptr p = make_shared();
    p->x = 3, p->y = 4;
    cout << *p << endl;
    cout << "p.use_count = " << p.use_count() << endl;

    shared_ptr q = p;
    cout << "p.use_count = " << p.use_count() << endl;
    cout << "q.use_count = " << q.use_count() << endl;
    p = nullptr;
    cout << "q.use_count = " << q.use_count() << endl;
    q = nullptr;
    cout << "end main" << endl;

    return 0;
}

ENDS(test3)

BEGINS(test4)
class A {
public:
    A() {
        cout << "default constructor" << endl;
    }
    int x, y;
    ~A() {
        cout << "destructor" << endl;
    }
};

class shared_ptr {
public:
    shared_ptr(A *ptr, int *cnt = nullptr) : ptr(ptr), cnt(cnt) {
        if (cnt == nullptr) {
            this->cnt = new int(1);
        }
    }
    shared_ptr(const shared_ptr &obj) : ptr(obj.ptr), cnt(obj.cnt) {
        (*cnt)++;
    }
    ~shared_ptr() {
        decrease_one();
    }
    A *operator->() { return ptr; }
    A &operator*() { return *ptr; }
    shared_ptr &operator=(const shared_ptr &obj) {
        if (obj.ptr == ptr && obj.cnt == cnt) return *this;
        decrease_one();
        ptr = obj.ptr;
        cnt = obj.cnt;
        (*cnt)++;
        return *this;
    }
    int use_count() { return *(cnt); }
private:
    A *ptr;
    int *cnt;
    void decrease_one() const {
        (*cnt)--;
        if ((*cnt) == 0) {
            delete ptr;
            delete cnt;
        }
    }
};

shared_ptr make_shared() {
    return shared_ptr(new A());
}

ostream &operator<<(ostream &out, const A &a) {
    cout << "class A : " << a.x << ", " << a.y;
    return out;
}

int main() {
    shared_ptr p = make_shared();
    p->x = 3, p->y = 4;
    cout << *p << endl;
    cout << "p.use_count = " << p.use_count() << endl;

    shared_ptr q = p;
    cout << "p.use_count = " << p.use_count() << endl;
    cout << "q.use_count = " << q.use_count() << endl;
    p = nullptr;
    cout << "q.use_count = " << q.use_count() << endl;
    q = nullptr;
    cout << "end main" << endl;

    return 0;
}

ENDS(test4)

int main() {
//    test1::main();
//    test2::main();
//    test3::main();
    test4::main();

    return 0;
}